import itertools
import logging
from xml.etree import ElementTree

import html5lib
from html5lib import treebuilders
from httplib2 import Http
import keytree

from pylons import request, response, session, tmpl_context as c
from pylons.controllers.util import abort, redirect_to

from therethere.controllers import BaseController, render

log = logging.getLogger(__name__)

KML = 'application/vnd.google-earth.kml+xml'

class Error(Exception):
    pass
    
class ThereThereError(Error):
    def __init__(self, msg):
        self.message = msg
    def __str__(self):
        return self.message
        
class LocateController(BaseController):

    def index(self):
        # Return a rendered template
        if 'locate:submit' in request.params:
            c.located = True
            c.url = request.params['locate:url']
            c.doc = Locator()(c.url)
        return render('/locate.html')

class Locator(object):
    
    def __call__(self, url):
        """Returns an ElementTree."""
        
        parser = html5lib.HTMLParser(
                    tree=treebuilders.getTreeBuilder('etree', ElementTree)
                    )

        resp, data = Http().request(url)
        if not resp['content-type'].startswith('text/html'):
            raise ThereThereError, "Unsupported media type: %s" % resp['content-type']
        
        doc = ElementTree.Element('doc')
        doc.attrib['href'] = url
        doc.attrib['type'] = resp['content-type']

        tree = parser.parse(data)

        # First, check for a KML alternate
        for e in itertools.ifilter(lambda x: x.attrib.get('rel') == 'alternate' and x.attrib.get('type') == KML, tree.findall('.//link')):
            alt = ElementTree.SubElement(doc, 'alt')
            alt.attrib = e.attrib.copy()
            kresp, kdata = Http().request(alt.attrib['href'])
            geoms = ElementTree.SubElement(alt, 'geoms')
            geoms.text = str(Bounds()(kdata))
        
        # Next, where links
        for link in itertools.ifilter(lambda x: x.attrib.get('rel') == 'where', itertools.chain(tree.findall('.//link'), tree.findall('.//a'))):
            # Handle KML
            if link.attrib.get('type') == KML:
                try:
                    where = ElementTree.SubElement(doc, 'where')
                    where.attrib = link.attrib.copy()
                    kresp, kdata = Http().request(link.attrib['href'])
                    geoms = ElementTree.SubElement(where, 'geoms')
                    geoms.text = str(Bounds()(kdata))
                except:
                    log.error("Failed to fetch or parse KML data from %s" % str(link))
            # In the case of HTML, fetch and look for a KML alternate
            else:
                try:
                    where = ElementTree.SubElement(doc, 'where')
                    where.attrib = link.attrib.copy()
                    wresp, wdata = Http().request(link.attrib.get('href'))
                    wtree = parser.parse(wdata)
                    for e in itertools.ifilter(lambda x: x.attrib.get('rel') == 'alternate' and x.attrib.get('type') == KML, wtree.findall('.//link')):
                        alt = ElementTree.SubElement(where, 'alt')
                        alt.attrib = e.attrib.copy()
                        kresp, kdata = Http().request(alt.attrib['href'])
                        geoms = ElementTree.SubElement(alt, 'geoms')
                        geoms.text = str(Bounds()(kdata))
                except:
                    log.error("Failed to fetch or parse KML data from %s" % str(link))
                    
        # Next, elsewhere links
        for link in itertools.ifilter(lambda x: x.attrib.get('rel') == 'elsewhere', itertools.chain(tree.findall('.//link'), tree.findall('.//a'))):
            # Handle KML
            if link.attrib.get('type') == KML:
                try:
                    where = ElementTree.SubElement(doc, 'elsewhere')
                    where.attrib = link.attrib.copy()
                    kresp, kdata = Http().request(link.attrib['href'])
                    geoms = ElementTree.SubElement(where, 'geoms')
                    geoms.text = str(Bounds()(kdata))
                except:
                    log.error("Failed to fetch or parse KML data from %s" % str(link))
            # In the case of HTML, fetch and look for a KML alternate
            else:
                try:
                    where = ElementTree.SubElement(doc, 'elsewhere')
                    where.attrib = link.attrib.copy()
                    wresp, wdata = Http().request(link.attrib.get('href'))
                    wtree = parser.parse(wdata)

                    for e in itertools.ifilter(lambda x: x.attrib.get('rel') == 'alternate' and x.attrib.get('type') == KML, wtree.findall('.//link')):
                        alt = ElementTree.SubElement(where, 'alt')
                        alt.attrib = e.attrib.copy()
                        kresp, kdata = Http().request(alt.attrib['href'])
                        geoms = ElementTree.SubElement(alt, 'geoms')
                        geoms.text = str(Bounds()(kdata))
                except:
                    log.error("Failed to fetch or parse KML data from %s" % str(link))
        
        return doc

class Bounds(object):
    
    def __call__(self, data):
        tree = ElementTree.fromstring(data)
        kmlns = tree.tag.split('}')[0][1:]
        geoms = []
        for p in tree.findall('.//{%s}Placemark' % kmlns):
            g = keytree.feature(p).geometry
            geoms.append(dict(type=g.type, coordinates=g.coordinates))
        return geoms
